#include <iostream>
#include <assert.h>
#include "instruction.h"
#include "helper.h"

/////////////
// CInstruction

CInstruction::CInstruction(CInstruction *p) : prev(p)
{

}

/////////////
// CInstruction1arg

CInstruction1arg::CInstruction1arg(CInstruction *prev, opType op1) : CInstruction(prev)
{
	ops[0].type = op1;
}

std::string CInstruction1arg::getCode()
{
	return name + " " + ops[0].GetCode();
}

CInstruction* CInstruction1arg::copy()
{
	CInstruction *i = new CInstruction1arg(*this);
	return i;
}

void CInstruction1arg::GetRandomOperands(SGlobalInfo &info)
{
	ops[0].GetRandomOperand();
}

int CInstruction1arg::getDestReg(int* destReg)
 {
     if (ops[0].type == REG)
     {
        *destReg=ops[0].index;
        return 1;
     }
     return 0;
 };

int CInstruction1arg::getSrcReg(int* srcRegs)
 {
     return 0;
 };


/////////////
// CInstruction2args
CInstruction2args::CInstruction2args(CInstruction *prev, opType op1, opType op2) : CInstruction(prev)
{
ops[0].type = op1;
ops[1].type = op2;
}

CInstruction2args::CInstruction2args(CInstruction *prev, unsigned int reg, unsigned int value) : CInstruction(prev)
{
	ops[0].type = REG;
	ops[0].index = reg;
	ops[1].type = IMM;
	ops[1].index = value;
}

std::string CInstruction2args::getCode()
{
	return name + " " + ops[0].GetCode() + ", " + ops[1].GetCode();
}

CInstruction* CInstruction2args::copy()
{
	CInstruction *i = new CInstruction2args(*this);
	return i;
}

void CInstruction2args::GetRandomOperands(SGlobalInfo &info)
{
	ops[0].GetRandomOperand();
	ops[1].GetRandomOperand();
}

int CInstruction2args::getDestReg(int* destReg)
 {
     std::string store = "STR";
     if ((ops[0].type == REG)&& (name != store))
     {
        *destReg=ops[0].index;
        return 1;
     }
     return 0;
 };

int CInstruction2args::getSrcReg(int* srcRegs)
 {
     if (ops[1].type == REG)
     {
        srcRegs[0]=ops[1].index;
        return 1;
     }
     std::string store = "STR";
     if (name == store)
     {
        srcRegs[0]=ops[0].index;
        return 1;
     }

     return 0;
 };


/////////////
// CInstruction3args
CInstruction3args::CInstruction3args(CInstruction *prev, opType op1, opType op2, opType op3) : CInstruction(prev)
{
	ops[0].type = op1;
	ops[1].type = op2;
	ops[2].type = op3;
}

CInstruction3args::CInstruction3args(CInstruction *prev, unsigned int reg, unsigned int value) : CInstruction(prev)
{
	ops[0].type = REG;
	ops[0].index = reg;
	ops[1].type = REG;
	ops[1].index = reg;
	ops[2].type = IMM;
	ops[2].index = value;
}

std::string CInstruction3args::getCode()
{
	return name + " " + ops[0].GetCode() + ", " + ops[1].GetCode() + ", " + ops[2].GetCode();
}

CInstruction* CInstruction3args::copy()
{
	CInstruction *i = new CInstruction3args(*this);
	return i;
}

void CInstruction3args::GetRandomOperands(SGlobalInfo &info)
{
	ops[0].GetRandomOperand();
	ops[1].GetRandomOperand();
	ops[2].GetRandomOperand();
}

int CInstruction3args::getDestReg(int* destReg)
 {

     if (ops[0].type == REG)
     {
        *destReg=ops[0].index;
        return 1;
     }
     return 0;
 };

int CInstruction3args::getSrcReg(int* srcRegs)
 {
    int noRegOps = 0;
     if (ops[1].type == REG)
     {
        srcRegs[0]=ops[1].index;
        ++noRegOps;
     }
     if (ops[2].type == REG)
     {
        srcRegs[1]=ops[2].index;
        ++noRegOps;
     }
     return noRegOps;
 };

/////////////
// CInstructionBranch
CInstructionBranch::CInstructionBranch(CInstruction *prev) : CInstruction(prev)
{

}

std::string CInstructionBranch::getCode()
{
	return name + " " + branchName;
}

CInstruction* CInstructionBranch::copy()
{
	CInstruction *i = new CInstructionBranch(*this);
	return i;
}

void CInstructionBranch::GetRandomOperands(SGlobalInfo &info)
{
	const unsigned int loopCountMax = 127;  //maximum loop amount
	const unsigned int maxLoopRegisters = 13; //biggest value of backward branches
	const unsigned int BRANCH_BITS = 24;    //Number of branchbits
	static unsigned int currentBranch = 0; //keeps track of the current branch name
	static unsigned int branchRegister = 8; //keeps track of which registers to use for branches

	unsigned int branchTarget = rand() % ((unsigned int)MIN(info.instructionCount, pow(2, BRANCH_BITS)) - 1) + 1; //do not allow branches to 0
	if(info.currentInstruction == 0)
		branchTarget = 1;

	if(branchRegister > maxLoopRegisters)
	{
		//too many backward branches
		branchTarget = rand() % (info.instructionCount - info.currentInstruction ) + info.currentInstruction + 1;
		//std::cout << "Too many backward branches generated, only generate forward now" << std::endl;
		//std::cout << "Target: " << branchTarget << " currInstr: " << info.currentInstruction << " instrCount: " << info.instructionCount << std::endl;

	}

	if(branchTarget > info.currentInstruction)
	{
		//forward branch, ok
		branchName = "Forward" + Convert<std::string>(currentBranch);

		info.branchAdded = true;
		info.branchBackward = false;
		info.branchPosition = branchTarget;
		info.branchName = branchName;

		name = name + BRANCH_CONDITIONS[rand() % CONDITION_LENGTH];

		++currentBranch;
	}
	else
	{
		//branch is backwards,
		assert(branchRegister <= maxLoopRegisters);  //just for debugging

		name = name + "PL";   //PL = branch on positive
		branchName = "Back" + Convert<std::string>(currentBranch);

		info.branchAdded = true;
		info.branchPosition = branchTarget;
		info.branchName = branchName;

		info.branchBackward = true;
		info.regName = branchRegister;
		info.startValue = (rand() % loopCountMax);

		++branchRegister;
		++currentBranch;
	}
}

/////////////
// CInstructionLabel
CInstructionLabel::CInstructionLabel(CInstruction *prev, std::string name) : CInstruction(prev)
{
	labelName = name;
}

std::string CInstructionLabel::getCode()
{
	return labelName + ":";
}

CInstruction* CInstructionLabel::copy()
{
	CInstruction *i = new CInstructionLabel(*this);
	return i;
}

void CInstructionLabel::GetRandomOperands(SGlobalInfo &info)
{
}

/////////////
// COperand

void CInstruction::COperand::GetRandomOperand()
{
//enum opType {NA, REG, MEM, IMM, REGMEM, REGIMM, ALL};

	int i;
	switch(type)
	{
		case REG:
			GetRegOperand();
			break;
		case MEM:
			GetMemOperand();
			break;
		case IMM:
			GetImmOperand();
			break;
		case REGMEM:
			if (rand() % 2 == 1)
				GetRegOperand();
			else
				GetMemOperand();
			break;
		case REGIMM:
			if (rand()%2==1)
				GetRegOperand();
			else
				GetImmOperand();
			break;
		case ALL:
			if ((i = rand()%3) == 2)
				GetRegOperand();
			else if (i == 1)
				this->GetMemOperand();
			else
				GetImmOperand();
			i = 0;
			break;
		case NA:
			GetRegOperand();
	}
	return;
}

void CInstruction::COperand::GetRegOperand()
{
	const int registersToUse = 5;
	type = REG;
	index = rand()%registersToUse;
	return;
}

void CInstruction::COperand::GetMemOperand()
{
	const int memLocsToUse = 3;
	type = MEM;
	index = rand()%memLocsToUse;
	return;
}

void CInstruction::COperand::GetImmOperand()
{
	const int immRange = 128;
	type = IMM;
	index = rand()%immRange;
	return;
}


std::string CInstruction::COperand::GetCode()
{
	std::stringstream codestream;
	switch(type)
	{
	case REG:
		codestream << "R" << index;
		break;
	case IMM:
		codestream << "#" << index;
		break;
	case MEM:
		codestream << "[fp, #-" <<  4*index + 16 << "]";
		break;
	default:
		codestream << "ERROR";
	}
	return codestream.str();
}

